using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.MSBuild;

namespace CSharpAnalyzer.AnalyzerService.Workspace;

/// <summary>
/// Loads C# solutions using MSBuildWorkspace
/// </summary>
public class WorkspaceLoader : IDisposable
{
    private readonly MSBuildWorkspace _workspace;

    /// <summary>
    /// Initializes a new instance of the WorkspaceLoader
    /// </summary>
    public WorkspaceLoader()
    {
        _workspace = MSBuildWorkspace.Create();

        // Log workspace failures for debugging
        _workspace.WorkspaceFailed += (sender, args) =>
        {
            Console.WriteLine($"Workspace diagnostic: {args.Diagnostic.Kind} - {args.Diagnostic.Message}");
        };
    }

    /// <summary>
    /// Loads a solution from the specified path
    /// </summary>
    /// <param name="solutionPath">Absolute path to the .sln file</param>
    /// <param name="cancellationToken">Cancellation token</param>
    /// <returns>The loaded solution</returns>
    /// <exception cref="FileNotFoundException">Thrown when solution file doesn't exist</exception>
    /// <exception cref="InvalidOperationException">Thrown when solution cannot be loaded</exception>
    public async Task<Solution> LoadSolutionAsync(string solutionPath, CancellationToken cancellationToken = default)
    {
        if (string.IsNullOrWhiteSpace(solutionPath))
        {
            throw new ArgumentException("Solution path cannot be null or empty", nameof(solutionPath));
        }

        if (!File.Exists(solutionPath))
        {
            throw new FileNotFoundException($"Solution file not found: {solutionPath}");
        }

        try
        {
            Console.WriteLine($"Loading solution: {solutionPath}");
            var solution = await _workspace.OpenSolutionAsync(solutionPath, cancellationToken: cancellationToken);

            if (solution == null)
            {
                throw new InvalidOperationException("Failed to load solution");
            }

            Console.WriteLine($"Solution loaded successfully: {solution.FilePath}");
            Console.WriteLine($"Projects count: {solution.Projects.Count()}");

            return solution;
        }
        catch (Exception ex) when (ex is not FileNotFoundException)
        {
            throw new InvalidOperationException($"Error loading solution: {ex.Message}", ex);
        }
    }

    /// <summary>
    /// Gets all documents from a solution
    /// </summary>
    /// <param name="solution">The solution to extract documents from</param>
    /// <returns>All documents in the solution</returns>
    public IEnumerable<Document> GetAllDocuments(Solution solution)
    {
        if (solution == null)
        {
            throw new ArgumentNullException(nameof(solution));
        }

        return solution.Projects.SelectMany(p => p.Documents);
    }

    /// <summary>
    /// Gets all documents from specific projects
    /// </summary>
    /// <param name="solution">The solution to extract documents from</param>
    /// <param name="projectNames">Names of projects to include</param>
    /// <returns>Documents from specified projects</returns>
    public IEnumerable<Document> GetDocumentsFromProjects(Solution solution, IEnumerable<string> projectNames)
    {
        if (solution == null)
        {
            throw new ArgumentNullException(nameof(solution));
        }

        if (projectNames == null || !projectNames.Any())
        {
            return GetAllDocuments(solution);
        }

        var projectNameSet = new HashSet<string>(projectNames, StringComparer.OrdinalIgnoreCase);
        var matchedProjects = solution.Projects.Where(p => projectNameSet.Contains(p.Name));

        return matchedProjects.SelectMany(p => p.Documents);
    }

    /// <summary>
    /// Disposes the workspace
    /// </summary>
    public void Dispose()
    {
        _workspace?.Dispose();
    }
}
